﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Swift;
using SCM;
using Swift.Math;
using UnityEngine.AI;
using System;

public class MapGround : SCMBehaviour, IEventHandler
{
    public UnitCreator UC;
    public EffectCreator EC;
    public UnitsUI UUIs;
    Texture2D VisionTex;
    
    public GameObject Obstacle;
    public GameObject SelectionArea;
    public GameObject RadarSelFlag;
    public GameObject BaseLine;

    public NavMeshAgent NMASmall;
    public NavMeshAgent NMAMedium;

    List<GameObject> ObstacleObjs = new List<GameObject>();

    public MainCamera MainCam = null;

    public List<List<Unit>> groups = new List<List<Unit>>();

    Dictionary<Unit, List<Unit>> b2u = new Dictionary<Unit, List<Unit>>();

    public bool CheckInVision(Vec2 pos)
    {
        return true;
        var vt = MainCam.GetVisionTex();
        
        if (VisionTex == null)
            VisionTex = new Texture2D(vt.width, vt.height);

        RenderTexture oldRT = RenderTexture.active;
        RenderTexture.active = vt;
        VisionTex.ReadPixels(new Rect(0, 0, vt.width, vt.height), 0, 0, false);
        RenderTexture.active = oldRT;
        var a = VisionTex.GetPixel((int)pos.x, (int)pos.y).a;
        return a > 0.2f;
    }

    protected override void StartOnlyOneTime()
    {
        Room4Client.OnBattleBegin += OnBattleBegin;
        Room4Client.OnBattleEnd += OnBattleEnd;
        Room4Client.NotifyAddBattleUnit += NotifyAddBattleUnit;
        Room4Client.NotifyAddBuildingUnit += NotifyAddBuildingUnit;
        Room4Client.NotifyUnitRemoved += NotifyUnitRemoved;
        Room4Client.NotifyAddObstacle += NotifyAddObstacle;
    }

    private void NotifyAddObstacle(Vec2[] vertices)
    {
        // 构建障碍显示

        var obs = Instantiate(Obstacle) as GameObject;
        obs.transform.SetParent(transform.parent);
        obs.SetActive(true);
        ObstacleObjs.Add(obs);

        var lr = obs.GetComponentInChildren<LineRenderer>();
        var vs = new List<Vec2>(vertices);
        if (vertices.Length > 2)
            vs.Add(vertices[0]);

        var pts = vs.ToArray((i, v2, skipIt) => new Vector3((float)v2.x, 0, (float)v2.y));
        lr.positionCount = pts.Length;
        lr.SetPositions(pts);
        
        // 构建碰撞体 mesh

        var m = new Mesh();

        var ptsUp = vs.ToArray((i, v2, skipIt) => new Vector3((float)v2.x, 20, (float)v2.y));
        var meshVs = new List<Vector3>();
        meshVs.AddRange(pts);
        meshVs.AddRange(ptsUp);
        m.SetVertices(meshVs);

        var ts = new List<int>();
        FC.For(pts.Length - 1, (i) =>
        {
            var p1 = pts.Length + i;
            var p2 = i;
            var p3 = i + 1;
            var p4 = pts.Length + i + 1;
            ts.Add(p1); ts.Add(p2); ts.Add(p3);
            ts.Add(p3); ts.Add(p4); ts.Add(p1);
        });

        m.SetTriangles(ts, 0);
        m.RecalculateBounds();
        obs.AddComponent<MeshCollider>().sharedMesh = m;

        foreach (var nms in GetComponents<NavMeshSurface>())
            nms.BuildNavMesh();
    }

    private void NotifyUnitRemoved(Unit u)
    {
        RemoveUnitFromGroup(u);

        if (u.cfg.IsBuilding)
        {
            foreach (var nms in GetComponents<NavMeshSurface>())
                nms.BuildNavMesh();
        }
    }

    private void NotifyAddBuildingUnit(Unit u)
    {
        foreach (var nms in GetComponents<NavMeshSurface>())
            nms.BuildNavMesh();

        b2u[u] = new List<Unit>();
        UC.GetModel(u.UID).Selected = false;
    }

    void MoveStraightForward(Unit u)
    {
        var dst1 = new Vec2(u.Pos.x, u.Player == 1 ? room.MapSize.y - 150 : 150);
        var dst2 = new Vec2(room.MapSize.x / 2, u.Player == 1 ? room.MapSize.y - 150 : 150);

        if (u.Player == 1 ? u.Pos.y >= room.MapSize.y - 150 : u.Pos.y <= 150)
            MoveOnPath(u, new Vec2[] { dst2 }, false);
        else
            MoveOnPath(u, new Vec2[] { dst1, dst2 }, false);


    }

    public void SwitchMode(Unit building)
    {
        if (b2u.ContainsKey(building))
        {
            foreach (var u in b2u[building])
                MoveStraightForward(u);

            b2u.Remove(building);
        }
        else
            b2u[building] = new List<Unit>();

        UC.GetModel(building.UID).Selected = !b2u.ContainsKey(building);
    }

    private void NotifyAddBattleUnit(Unit building, Unit u)
    {
        if (u.cfg.MaxVelocity <= 0 || u.UnitType == "SoldierCarrier")
            return;

        MoveStraightForward(u);

        //if (building != null)
        //{
        //    if (b2u.ContainsKey(building))
        //        b2u[building].Add(u);
        //    else
        //        MoveStraightForward(u);
        //}

        return;

        if (u.Player != GameCore.Instance.MePlayer
            || u.cfg.MaxVelocity == 0 || u.cfg.OutOfControl)
            return;

        if (groups.Count == 0)
        {
            units2Move = new List<Unit>();
            groups.Add(units2Move);
        }

        var g = groups[0];
        g.Add(u);
        if (g.Count > 1 && building != null) // 不是建筑生产出来的部队，不自动归队
            Move2(u, g[0].Pos, false);

        if (g == units2Move)
            SetCurrentGroup(g);
    }

    private void OnBattleBegin(Room4Client r, bool inReplay)
    {
        BaseLine.SetActive(true);
        Room = r;
        var mapSize = r.MapSize;
        foreach (var nms in GetComponents<NavMeshSurface>())
            nms.BuildNavMesh();
    }

    private void OnBattleEnd(Room r, string winner, bool inReplay)
    {
    }

    public Room4Client Room
    {
        get { return room; }
        set
        {
            room = value;
            room.PathFinder = FindPath;
            var sz = room.MapSize;
            var w = sz.x;
            var h = sz.y;
            var cx = w / 2;
            var cy = h / 2;

            // 地表模型需要稍微扩大一点，因为 navmesh 会缩进一部分让地图边缘无法寻路。
            // 选 50 是因为大于所有寻路层的 Agent 半径(最大的是 medium 的 36)
            var gw = (float)w + 50 * 2;
            var gh = (float)h + 50 * 2;
            transform.localScale = new Vector3(gw, 1, gh);
            transform.localPosition = new Vector3((float)cx, 0, (float)cy);
        }
    } Room4Client room;

    Unit PickMyBattleUnitAt(Vec2 pt)
    {
        Unit tar = null;
        Fix64 selDist = 40;
        const float airforceHeight = 60; // 飞机机身高度，这里需要特殊处理下点选，不然飞机相应点在影子不在机身
        foreach (var u in room.AllUnits)
        {
            if (u.Player == GameCore.Instance.MePlayer
                && u.cfg.MaxVelocity > 0
                && !u.cfg.OutOfControl)
            {
                var p = u.Pos;
                if (u.cfg.IsAirForce) // 根据摄像机参数，修正相应点
                {
                    var uim = UIManager.Instance;
                    var uip = uim.World2View(new Vector3((float)p.x, airforceHeight, (float)p.y));
                    p = uim.View2Ground(uip);
                }

                var dist = (p - pt).Length;
                if (dist < selDist)
                {
                    selDist = dist;
                    tar = u;
                }
            }
        }

        return tar;
    }

    // 单击地面，移动所有战斗单位，并搜索攻击目标
    List<Unit> units2Move = null;
    public void OnClick(Vec2 pt, Vector3 wp)
    {
        // SelConstructBuilding(pt, () => new Vec2(wp.x, wp.z));
        if (UUIs.CurUnitType != null)
        {
            // 只能在各自那边建造
            if (UUIs.CurUnitType != "SoldierCarrier")
            {
                var cp = wp;
                if (cp.z > room.MapSize.y / 2 && GameCore.Instance.MePlayer == 1)
                {
                    AddTip("只能在自己一侧放置");
                    return;
                }
                else if (cp.z < room.MapSize.y / 2 && GameCore.Instance.MePlayer == 2)
                {
                    AddTip("只能在自己一侧放置");
                    return;
                }
            }

            var cfg = UnitConfiguration.GetDefaultConfig(UUIs.CurUnitType);

            if (!UUIs.CheckColdown(UUIs.CurUnitType))
            {
                AddTip("等待冷却");
                return;
            }
            
            if (GameCore.Instance.GetMyResource("Money") < cfg.Cost)
            {
                AddTip("资源不足");
                return;
            }

            UUIs.ResetColdown(UUIs.CurUnitType);
            var conn = GameCore.Instance.ServerConnection;
            if (UUIs.CurUnitType == "SoldierCarrier")
            {
                var buff = conn.Send2Srv("DropSoldierFromCarrier");
                buff.Write(new Vec2(wp.x, wp.z));
                conn.End(buff);
            }
            else
            {
                var buff = conn.Send2Srv("AddBattleUnitAt");
                buff.Write(UUIs.CurUnitType);
                buff.Write(new Vec2(wp.x, wp.z));
                conn.End(buff);
            }
        }

        return;

        // 当前尚未有选中的组就尝试选中
        var tar = PickMyBattleUnitAt(new Vec2(wp.x, wp.z));
        if (tar == null)
        {
            if (units2Move != null && units2Move.Count > 0)
            {
                // 当前有选中的组，就指挥移动
                foreach (var u in units2Move)
                    Move2(u, new Vec2(wp.x, wp.z), false);
            }

            EC.CreateEffect("ClickEffect", EC.transform, wp, 1);
        }
        else
        {
            var g = FindGroup(tar);
            SetCurrentGroup(g);
        }
    }

    // 统计我方和敌方具备战斗能力的单位的中心位置
    bool CalculateUnitsCenterPos(out Vec2 my, out Vec2 enemy, bool includingCanAttack)
    {
        my = Vec2.Zero;
        enemy = Vec2.Zero;
        var myCnt = 0;
        var enemyCnt = 0;

        var me = GameCore.Instance.MePlayer;
        foreach (var u in Room.AllUnits)
        {
            if (u.cfg.OutOfControl || u.IsNeutral)
                continue;

            if (u.Player == me)
            {
                if (u.cfg.MaxVelocity > 0)
                {
                    myCnt++;
                    my += u.Pos;
                }
            }
            else if (includingCanAttack ? !u.cfg.Unattackable : u.cfg.Power > 0)
            {
                enemyCnt++;
                enemy += u.Pos;
            }
        }

        if (enemyCnt == 0)
            return false;

        if (myCnt != 0)
            my /= myCnt;

        enemy /= enemyCnt;
        return true;
    }

    // 选中所有战斗单位，并合并为一组
    public void SelAllBattleUnit()
    {
        BaseLine.SetActive(false);
        groups.Clear();
        units2Move = new List<Unit>();
        groups.Add(units2Move);
        foreach (var u in room.AllUnits)
        {
            if (u.cfg.OutOfControl || u.Player != GameCore.Instance.MePlayer ||
                u.cfg.MaxVelocity == 0)
                continue;

            units2Move.Add(u);
            UC.GetModel(u.UID).Selected = true;
        }
    }

    // 双击地面，移动所有战斗单位，并忽略攻击目标
    public void OnDoubleClick(Vec2 pt, Vector3 wp)
    {
        OnClick(pt, wp);
    }

    // 客户端利用 unity 的 navmesh 寻路
    Vec2[] FindPath(Vec2 src, Vec2 dst, Fix64 size)
    {
        if (Room == null)
            return null;

        if (!src.InRect(Room.MapSize) || !dst.InRect(Room.MapSize))
            return null;

        var nma = size <= 15 ? NMASmall : NMAMedium;
        nma.Warp(new Vector3((float)src.x, 0, (float)src.y));
        nma.gameObject.SetActive(true);
        var navPath = new NavMeshPath();
        bool finded = !nma.isOnNavMesh ? false : nma.CalculatePath(new Vector3((float)dst.x, 0, (float)dst.y), navPath);
        nma.gameObject.SetActive(false);

        if (!finded)
            return null;

        var path = navPath.corners.ToArray((i, pn, skip) =>
        {
            if (i == 0) // skip the first path node since it's always the unit current position
            {
                skip();
                return Vec2.Zero;
            }
            else
                return new Vec2(pn.x, pn.z);
        });

        return path;
    }

    // 沿着给定路径走
    public void MoveOnPath(Unit u, Vec2[] path, bool ignoreTarget)
    {
        if (u.cfg.MaxVelocity <= 0 || path == null || path.Length == 0)
            return;

        SendPath(u, path, ignoreTarget);
    }

    public void Move2(Unit u, Vec2 pt, bool ignoreTarget)
    {
        if (u.Player != GameCore.Instance.MePlayer || u.cfg.MaxVelocity <= 0)
            return;

        // 空中单位走直线
        var path = u.cfg.IsAirForce ? new Vec2[] { pt } : FindPath(u.Pos, pt, u.cfg.RealSize);
        if (path == null)
            return;

        SendPath(u, path, ignoreTarget);
    }

    public void SelConstructBuilding(Vec2 pt, Func<Vec2> getConstructingPos)
    {
        if (!CheckInVision(pt))
        {
            AddTip("只能在视野内操作");
            return;
        }

        var choices = GameCore.Instance.MeInfo.AllCanBuildOnEmptyGround;
        if (choices.Length == 0)
            return;

        // 空地上长按进行选择建造建筑
        var ui = UIManager.Instance.ShowTopUI("InBattleUI/SelectUnitUI", true) as SelectUnitUI;
        ui.Pos = pt;
        ui.Choices = choices;
        ui.ChoicesName = ui.Choices.ToArray((i, c, skip) =>
        {
            var ccfg = Room.GetMyUnitConfig(c);
            return ccfg.DisplayName + "\r\n(" + ccfg.Cost + ")";
        });
        ui.Refresh();
        ui.OnChoiceSel = (buildingType) =>
        {
            if (!CheckInVision(pt))
            {
                AddTip("只能在视野内操作");
                return;
            }

            var cfg = Room.GetMyUnitConfig(buildingType);
            var cost = cfg.Cost;
            if (GameCore.Instance.GetMyResource("Money") < cost)
            {
                AddTip("资源不足");
                return;
            }
            else if (cfg.Prerequisites != null 
                    && room.GetAllMyUnits((u) => u.BuildingCompleted && cfg.Prerequisites.FirstIndexOf(u.UnitType) >= 0).Length == 0)
            {
                AddTip("需要先建造 " + Room.GetMyUnitConfig(cfg.Prerequisites[0]).DisplayName);
                return;
            }

            // 只能在各自那边建造
            var cp = getConstructingPos();
            if (cp.y > room.MapSize.y / 2 && GameCore.Instance.MePlayer == 1)
                return;
            else if (cp.y < room.MapSize.y / 2 && GameCore.Instance.MePlayer == 2)
                return;

            if (!room.CheckSpareSpace(cp.x, cp.y, cfg.ShowSize))
            {
                UIManager.Instance.Tips.AddTip("没有足够的建造空间");
                return;
            }

            var conn = GameCore.Instance.ServerConnection;
            var buff = conn.Send2Srv("ConstructBuilding");
            buff.Write(buildingType);
            buff.Write(cp);
            conn.End(buff);
        };
    }

    List<Vector3> dragPath = new List<Vector3>();
    Unit dragUnit = null;
    public void OnDragStarted(Vec2 pt, Vector3 wp)
    {
        dragPath.Clear();
        dragPath.Add(wp);
        var lr = SelectionArea.GetComponentInChildren<LineRenderer>();
        lr.positionCount = 0;

        dragUnit = PickMyBattleUnitAt(new Vec2(wp.x, wp.z));
        if (dragUnit != null)
        {
            RemoveUnitFromGroup(dragUnit);
            var g = new List<Unit>();
            g.Add(dragUnit);
            groups.Add(g);
            SetCurrentGroup(g);
        }

        SelectionArea.SetActive(dragUnit != null);
    }

    public void OnDragging(Vec2 from, Vector3 fromWp, Vec2 now, Vector3 nowWp)
    {
        if (dragUnit == null)
            return;

        if (dragPath.Count > 0)
        {
            var lastWp = dragPath[dragPath.Count - 1];
            var d = (nowWp - lastWp).magnitude;
            if (d < 20)
                return;
        }

        dragPath.Add(nowWp);
        var lr = SelectionArea.GetComponentInChildren<LineRenderer>();
        lr.positionCount = dragPath.Count;
        lr.SetPositions(dragPath.ToArray());
    }

    public void DoDragEnded(Vec2 from, Vector3 fromWp, Vec2 to, Vector3 toWp)
    {
        SelectionArea.SetActive(false);
        if (dragUnit == null || dragPath.Count < 2)
            return;

        dragPath.RemoveAt(0);
        SendPath(dragUnit, dragPath.ToArray((i, v3, skipAct) => { return new Vec2(v3.x, v3.z); }), true);
    }

    void SplitGroups(Vector3 lineStart, Vector3 lineEnd)
    {
        var gAdd = new List<Unit>();
        foreach (var g in groups)
        {
            var ng = SplitGroup(lineStart, lineEnd, g);
            if (ng != null)
                gAdd.AddRange(ng);
        }

        if (gAdd.Count > 0)
        {
            groups.Add(gAdd);
            SetCurrentGroup(gAdd);
        }
    }

    List<Unit> SplitGroup(Vector3 lineStart, Vector3 lineEnd, List<Unit> g)
    {
        var ng = new List<Unit>();
        foreach (var u in g.ToArray())
        {
            if (Geo.IsPointOnRightSide(lineStart.x, lineStart.z, lineEnd.x, lineEnd.z, u.Pos.x, u.Pos.y))
            {
                g.Remove(u);
                ng.Add(u);
            }
        }

        if (g.Count < ng.Count)
        {
            var arr = g.ToArray();
            g.Clear();
            g.AddRange(ng);
            ng.Clear();
            ng.AddRange(arr);
        }

        return ng.Count == 0 ? null : ng;
    }

    List<Unit> FindGroup(Unit u)
    {
        foreach (var g in groups)
        {
            if (g.Contains(u))
                return g;
        }

        return null;
    }

    void RemoveUnitFromGroup(Unit u)
    {
        var g = FindGroup(u);
        if (g == null)
            return;

        g.Remove(u);
        if (g.Count == 0)
            groups.Remove(g);
    }

    void MakeANewGroup(List<Unit> newOne)
    {
        groups.Add(newOne);
    }

    void SetCurrentGroup(List<Unit> g)
    {
        if (units2Move != null)
        {
            foreach (var u in units2Move)
            {
                var mu = UC.GetModel(u.UID);
                if (mu != null)
                    mu.Selected = false;
            }
        }

        units2Move = g;
        foreach (var u in units2Move)
            UC.GetModel(u.UID).Selected = true;
    }

    void SendPath(Unit u, Vec2[] path, bool ignoreTarget)
    {
        if (u.Player != GameCore.Instance.MePlayer || u.cfg.OutOfControl)
            return;

        var uid = u.UID;
        var conn = GameCore.Instance.ServerConnection;
        var buff = conn.Send2Srv("SetPath");
        buff.Write(uid);
        buff.Write(path);
        buff.Write(ignoreTarget);
        conn.End(buff);
    }

    public void OnPress(Vec2 pt, Vector3 wp)
    {
        // 只能在各自那边建造
        var cp = wp;
        if (cp.z > room.MapSize.y / 2 && GameCore.Instance.MePlayer == 1)
        {
            AddTip("只能在自己一侧建造");
            return;
        }
        else if (cp.z < room.MapSize.y / 2 && GameCore.Instance.MePlayer == 2)
        {
            AddTip("只能在自己一侧建造");
            return;
        }

        SelConstructBuilding(pt, () => new Vec2(wp.x, wp.z));
    }

    public void Clear()
    {
        foreach (var obs in ObstacleObjs)
            Destroy(obs);
        ObstacleObjs.Clear();

        transform.localScale = Vector3.zero;
        transform.localPosition = Vector3.zero;

        foreach (var nms in GetComponents<NavMeshSurface>())
            nms.BuildNavMesh();

        MainCam.Clear();
        if (units2Move != null)
            units2Move.Clear();
    }

    void AddTip(string tip)
    {
        UIManager.Instance.Tips.AddTip(tip);
    }

    void AddSmallTip(string tip)
    {
        UIManager.Instance.Tips.AddSmallTip(tip);
    }
}
